/*
 * Monolithium Unit Tests
 * vector_io.c
 *
 * Copyright (C) 2018 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <check.h>
#include <stdlib.h>
#include <sdk/vector.h>

static void setup(void)
{
}

static void teardown(void)
{
}

START_TEST(test_vector_io_simple_scatter)
{
    const char text[] = "The destiny of this string is to break apart into pieces.";
    const char correct_piece1[] = "The destiny";
    const char correct_piece2[] = " of this string";
    const char correct_piece3[] = " is to break apart into pieces.";
    char piece1[sizeof(correct_piece1)] = "";
    char piece2[sizeof(correct_piece2)] = "";
    char piece3[sizeof(correct_piece3)] = "";

    vector_buffer_t buffers[] = {
        { piece1, sizeof(piece1) - 1 },
        { piece2, sizeof(piece2) - 1 },
        { piece3, sizeof(piece3) - 1 },
    };

    vector_t vector;
    vector_init(&vector, buffers, sizeof(buffers) / sizeof(*buffers));
    vector_write_scatter(&vector, text, sizeof(text) - 1);
    vector_clear(&vector);

    ck_assert_str_eq(piece1, correct_piece1);
    ck_assert_str_eq(piece2, correct_piece2);
    ck_assert_str_eq(piece3, correct_piece3);
}
END_TEST

START_TEST(test_vector_io_simple_gather)
{
    char piece1[] = "And then ";
    char piece2[] = "come back together,";
    char piece3[] = " like nothing ever happened.";
    const char correct_text[] = "And then come back together, like nothing ever happened.";
    char text[sizeof(correct_text)] = "";

    vector_buffer_t buffers[] = {
        { piece1, sizeof(piece1) - 1 },
        { piece2, sizeof(piece2) - 1 },
        { piece3, sizeof(piece3) - 1 },
    };

    vector_t vector;
    vector_init(&vector, buffers, sizeof(buffers) / sizeof(*buffers));
    vector_read_gather(&vector, text, sizeof(text) - 1);
    vector_clear(&vector);

    ck_assert_str_eq(text, correct_text);
}
END_TEST

START_TEST(test_vector_io_multi_write_scatter)
{
    const char text[] = "The destiny of this string is to break apart into pieces.";
    const char correct_piece1[] = "The destiny";
    const char correct_piece2[] = " of this string";
    const char correct_piece3[] = " is to break apart into pieces.";
    char piece1[sizeof(correct_piece1)] = "";
    char piece2[sizeof(correct_piece2)] = "";
    char piece3[sizeof(correct_piece3)] = "";

    vector_buffer_t buffers[] = {
        { piece1, sizeof(piece1) - 1 },
        { piece2, sizeof(piece2) - 1 },
        { piece3, sizeof(piece3) - 1 },
    };

    vector_t vector;
    vector_init(&vector, buffers, sizeof(buffers) / sizeof(*buffers));
    int i;
    for (i = 0; i < sizeof(text) - 1; i++) vector_write_scatter(&vector, &text[i], 1);
    vector_clear(&vector);

    ck_assert_str_eq(piece1, correct_piece1);
    ck_assert_str_eq(piece2, correct_piece2);
    ck_assert_str_eq(piece3, correct_piece3);
}
END_TEST

START_TEST(test_vector_io_multi_read_gather)
{
    char piece1[] = "And then ";
    char piece2[] = "come back together,";
    char piece3[] = " like nothing ever happened.";
    const char correct_text[] = "And then come back together, like nothing ever happened.";
    char text[sizeof(correct_text)] = "";

    vector_buffer_t buffers[] = {
        { piece1, sizeof(piece1) - 1 },
        { piece2, sizeof(piece2) - 1 },
        { piece3, sizeof(piece3) - 1 },
    };

    vector_t vector;
    vector_init(&vector, buffers, sizeof(buffers) / sizeof(*buffers));
    int i;
    for (i = 0; i < sizeof(text) - 1; i++) vector_read_gather(&vector, &text[i], 1);
    vector_clear(&vector);

    ck_assert_str_eq(text, correct_text);
}
END_TEST

START_TEST(test_vector_io_dynamic_scatter)
{
    const char text[] = "Scattering strings in an unnecessarily complicated way is fun.";
    const char correct_piece1[] = "Scattering strings is fun.";
    const char correct_piece2[] = "in a complicated way ";
    const char correct_piece3[] = "n unnecessarily";
    char piece1[sizeof(correct_piece1)] = "";
    char piece2[sizeof(correct_piece2)] = "";
    char piece3[sizeof(correct_piece3)] = "";

    vector_buffer_t buffers[] = {
        { piece1, sizeof(piece1) - 1 },
        { piece2, sizeof(piece2) - 1 },
        { piece3, sizeof(piece3) - 1 },
    };

    vector_t vector;
    vector_init(&vector, NULL, 0);
    vector_insert_buffer(&vector, 0, &buffers[0]);
    vector_insert_buffer(&vector, 19, &buffers[1]);
    vector_insert_buffer(&vector, 23, &buffers[2]);
    vector_write_scatter(&vector, text, sizeof(text) - 1);
    vector_clear(&vector);

    ck_assert_str_eq(piece1, correct_piece1);
    ck_assert_str_eq(piece2, correct_piece2);
    ck_assert_str_eq(piece3, correct_piece3);
}
END_TEST

START_TEST(test_vector_io_dynamic_gather)
{
    char piece1[] = "Building is fun.";
    char piece2[] = "a string in a non-linear way ";
    char piece3[] = "from pieces ";
    const char correct_text[] = "Building a string from pieces in a non-linear way is fun.";
    char text[sizeof(correct_text)] = "";

    vector_buffer_t buffers[] = {
        { piece1, sizeof(piece1) - 1 },
        { piece2, sizeof(piece2) - 1 },
        { piece3, sizeof(piece3) - 1 },
    };

    vector_t vector;
    vector_init(&vector, NULL, 0);
    vector_insert_buffer(&vector, 0, &buffers[0]);
    vector_insert_buffer(&vector, 9, &buffers[1]);
    vector_insert_buffer(&vector, 18, &buffers[2]);
    vector_read_gather(&vector, text, sizeof(text) - 1);
    vector_clear(&vector);

    ck_assert_str_eq(text, correct_text);
}
END_TEST

TCase *tcase_vector_io_simple_create(void)
{
    TCase *tc = tcase_create("Simple");
    tcase_add_checked_fixture(tc, setup, teardown);
    tcase_add_test(tc, test_vector_io_simple_scatter);
    tcase_add_test(tc, test_vector_io_simple_gather);
    return tc;
}

TCase *tcase_vector_io_multi_create(void)
{
    TCase *tc = tcase_create("Multiple Reads/Writes");
    tcase_add_checked_fixture(tc, setup, teardown);
    tcase_add_test(tc, test_vector_io_multi_write_scatter);
    tcase_add_test(tc, test_vector_io_multi_read_gather);
    return tc;
}

TCase *tcase_vector_io_dynamic_create(void)
{
    TCase *tc = tcase_create("Dynamic");
    tcase_add_checked_fixture(tc, setup, teardown);
    tcase_add_test(tc, test_vector_io_dynamic_scatter);
    tcase_add_test(tc, test_vector_io_dynamic_gather);
    return tc;
}

Suite *suite_vector_io_create(void)
{
    Suite *suite = suite_create("Vectored I/O");
    suite_add_tcase(suite, tcase_vector_io_simple_create());
    suite_add_tcase(suite, tcase_vector_io_multi_create());
    suite_add_tcase(suite, tcase_vector_io_dynamic_create());
    return suite;
}

